﻿using PLCSharp.CoreModel.Prism;
using Prism.Commands;
using Prism.Events;
using System.Text;



namespace PLCSharp.Views.Connects.SerialPort
{
    public class SerialPortModel : ValidateBase
    {
        private readonly IEventAggregator _eventAggregator;

        public SerialPortModel(IEventAggregator eventAggregator)
        {
            Load.Execute();

            Ports = SerialPortService.GetPortNames();

            var parityListString = Enum.GetNames(typeof(Parity));
            var i = 0;
            foreach (var item in parityListString)
            {
                ParityList.Add(item, i);
                i++;
            }

            _eventAggregator = eventAggregator;
            _eventAggregator.GetEvent<BytesEvent>().Subscribe(DataSend, (filter) => filter.EventTarget.Contains("SerialPort"));

            _ = GetStatusAsync();
        }


        //全局事件发送数据
        private void DataSend(BytesEventData data)
        {
            if (data.Data.Length < 1) return;
            //不存在端口
            if (Array.IndexOf(Ports, data.DataTarget) < 0) return;
            _ = client.SendAsync(data.DataTarget, data.Data);
        }

        #region Port

        private SerialPortService client;

        private DelegateCommand _OpenPort;

        public DelegateCommand OpenPort =>
             _OpenPort ??= new DelegateCommand(ExecuteOpenPort);

        private void ExecuteOpenPort()
        {
            client = OpenClint();
        }

        private SerialPortService OpenClint()
        {
            var newclient = new SerialPortService(PortName, BaudRate, ParityValue, StopBits, DataBits);

            newclient.ReceiveBytes += ReceiveBytes;

            newclient.Log += Log;
            newclient.Open();
            return newclient;
        }

        private void Log(string log)
        {
            WriteLog("err", log);
        }

        private DelegateCommand _ClosePort;

        public DelegateCommand ClosePort =>
             _ClosePort ??= new DelegateCommand(ExecuteClosePort);

        private void ExecuteClosePort()
        {
            client.Close();
        }

        private int _StopBits = 1;

        public int StopBits
        {
            get { return _StopBits; }
            set { SetProperty(ref _StopBits, value); }
        }

        private string _SendString;

        public string SendString
        {
            get { return _SendString; }
            set { SetProperty(ref _SendString, value); }
        }

        private bool _PortIsOpen;

        public bool PortIsOpen
        {
            get { return _PortIsOpen; }
            set { SetProperty(ref _PortIsOpen, value); }
        }

        private string[] _Ports;

        public string[] Ports
        {
            get { return _Ports; }
            set { SetProperty(ref _Ports, value); }
        }

        public ObservableDictionary<string, int> ParityList { get; set; } = [];

        private int _ParityValue = 0;

        public int ParityValue
        {
            get { return _ParityValue; }
            set { SetProperty(ref _ParityValue, value); }
        }

        private string _PortName = "COM1";
        public string PortName
        {
            get { return _PortName; }
            set { SetProperty(ref _PortName, value); }
        }

        private int _BaudRate = 9600;

        public int BaudRate
        {
            get { return _BaudRate; }
            set { SetProperty(ref _BaudRate, value); }
        }

        private int _DataBits = 8;

        public int DataBits
        {
            get { return _DataBits; }
            set { SetProperty(ref _DataBits, value); }
        }

        private AsyncDelegateCommand _StringSend;

        public AsyncDelegateCommand StringSend =>
             _StringSend ??= new AsyncDelegateCommand(ExecuteStringSendAsync);


        private async Task ExecuteStringSendAsync()
        {
            client ??= OpenClint();

            var sendSucceed = await client.SendAsync(PortName, SendString);
            if (!sendSucceed)
                WriteLog(">>>", "发送超时");
            if (IsLog)
                WriteLog(">>>", SendString);
        }


        private void ReceiveBytes(byte[] bytes)
        {
            var rMsg = Encoding.Default.GetString(bytes);
            if (IsLog)
            {
                WriteLog("<<<", rMsg);
                WriteLogHex("<<<", bytes);
            }
        }



        private string _SendHex;

        public string SendHex
        {
            get { return _SendHex; }
            set { SetProperty(ref _SendHex, value); }
        }

        private AsyncDelegateCommand _HexSend;

        public AsyncDelegateCommand HexSend =>
             _HexSend ??= new AsyncDelegateCommand(ExecuteHexSendAsync);


        async private Task ExecuteHexSendAsync()

        {

            if (string.IsNullOrEmpty(_SendHex)) return;

            List<char> chars = [];

            foreach (var item in _SendHex)
            {
                if (item == 32) continue;
                if (item > 47 && item < 58
                 || item > 64 && item < 71
                 || item > 96 && item < 103)
                { chars.Add(item); continue; }
                WriteLogHex("err", $"发送失败，非法字符{item}");
                return;
            }
            //"6162630d0A"

            if (chars.Count < 2 || chars.Count % 2 != 0)
            {
                WriteLogHex("err", "发送失败,检查输入");
                return;
            }

            StringBuilder stringBuilder = new();
            for (int i = 0; i < chars.Count; i++)
            {
                stringBuilder.Append(chars[i]);
                if (i % 2 == 1) stringBuilder.Append('-');
            }

            var byteStrings = stringBuilder.ToString().Split('-'); //{"61", "62", "63", "0d", "0A"}
            List<byte> byteList = [];
            foreach (var item in byteStrings)
            {
                if (string.IsNullOrEmpty(item)) continue;
                byteList.Add((byte)Convert.ToInt32(item, 16));
            }
            var bytes = byteList.ToArray();
            WriteLogHex(">>>", bytes);
            _ = await client.SendAsync(PortName, bytes);
        }

        #endregion Port

        private DelegateCommand _HexToString;

        public DelegateCommand HexToString =>
             _HexToString ??= new DelegateCommand(ExecuteHexToString);

        private void ExecuteHexToString()
        {
            //"61 62 63 0d 0A"
            if (string.IsNullOrEmpty(_SendHex)) return;

            List<char> bytes = [];

            foreach (var item in _SendHex)
            {
                if (item == 32) continue;
                if (item > 47 && item < 58
                 || item > 64 && item < 71
                 || item > 96 && item < 103)
                { bytes.Add(item); continue; }
                SendString = $"转换失败，非法字符：{item}";
                return;
            }
            //"6162630d0A"

            if (bytes.Count < 2 || bytes.Count % 2 != 0)
            {
                SendString = "转换失败,检查输入";
                return;
            }

            StringBuilder stringBuilder = new();
            for (int i = 0; i < bytes.Count; i++)
            {
                stringBuilder.Append(bytes[i]);
                if (i % 2 == 1) stringBuilder.Append('-');
            }

            var byteStrings = stringBuilder.ToString().Split('-'); //{"61", "62", "63", "0d", "0A"}

            stringBuilder.Clear();
            foreach (var item in byteStrings)
            {
                if (string.IsNullOrEmpty(item)) continue;
                stringBuilder.Append((char)Convert.ToInt32(item, 16));
            }

            SendString = stringBuilder.ToString();
        }

        private DelegateCommand _StringToHex;

        public DelegateCommand StringToHex =>
             _StringToHex ??= new DelegateCommand(ExecuteStringToHex);

        private void ExecuteStringToHex()
        {
            if (string.IsNullOrEmpty(_SendString)) return;
            var bytes = Encoding.Default.GetBytes(_SendString); // abc -> {97, 98, 99} ( {0x61,0x62,0x63} )
            SendHex = BitConverter.ToString(bytes, 0).Replace("-", " "); //"61 62 63"
        }

        //消息

        #region Log

        private DelegateCommand _ClearLogs;

        public DelegateCommand ClearLogs =>
             _ClearLogs ??= new DelegateCommand(ExecuteClearLogs);

        private void ExecuteClearLogs()
        {
            LogString = "";
            LogHex = "";
        }

        private string _LogString;

        public string LogString
        {
            get { return _LogString; }
            set { SetProperty(ref _LogString, value); }
        }

        private string _LogHex;

        public string LogHex
        {
            get { return _LogHex; }
            set { SetProperty(ref _LogHex, value); }
        }
        private void WriteLog(string type, string msg)
        {
            LogString += $"{DateTime.Now:mm:ss:fff} {type} |{msg} \n";
        }
        private void WriteLogHex(string type, string msg)
        {
            LogHex += $"{DateTime.Now:mm:ss:fff} {type} |{msg} \n";
        }
        private void WriteLogHex(string type, byte[] bytes)
        {
            var hexmsg = BitConverter.ToString(bytes, 0).Replace("-", " ");
            LogHex += $"{DateTime.Now:mm:ss:fff} {type} |{hexmsg} \n";
        }

        private bool _IsLog = true;

        public bool IsLog
        {
            get { return _IsLog; }
            set { SetProperty(ref _IsLog, value); }
        }
        #endregion Log

        #region Command

        async private Task GetStatusAsync()
        {
            while (true)
            {
                await Task.Delay(100);
                if (client is null) continue;
                PortIsOpen = client.IsOpen;
            }
        }

        #endregion Command

        #region Config


        private DelegateCommand _Load;

        public DelegateCommand Load =>
            _Load ??= new DelegateCommand(ExecuteLoad);

        private void ExecuteLoad()
        {

        }

        private DelegateCommand _Save;

        public DelegateCommand Save =>
            _Save ??= new DelegateCommand(ExecuteSave);

        private void ExecuteSave()
        {

        }

        #endregion Config
    }

    //
    // 摘要:
    //     Specifies the parity bit for a System.IO.Ports.SerialPort object.
    public enum Parity
    {
        //
        // 摘要:
        //     No parity check occurs.
        None = 0,

        //
        // 摘要:
        //     Sets the parity bit so that the count of bits set is an odd number.
        Odd = 1,

        //
        // 摘要:
        //     Sets the parity bit so that the count of bits set is an even number.
        Even = 2,

        //
        // 摘要:
        //     Leaves the parity bit set to 1.
        Mark = 3,

        //
        // 摘要:
        //     Leaves the parity bit set to 0.
        Space = 4
    }
}